using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Serialization;

namespace CincinnatiLibraryCheckAvailability
{
    [Serializable()]
    public class ItemStatus
    {
        private int quantity;
        private string location;
        private DateTime mostRecentChange = DateTime.MinValue;
        private DateTime lastUpdated = DateTime.MinValue;
        private DateTime birthDate = DateTime.Now;
        private TimeSpan timeAvailable = new TimeSpan(0);
        private TimeSpan lifetime = new TimeSpan(0);
        private List<ItemStatus> subStatuses;

        private TimeSpan tempTime = new TimeSpan(0);
        
        public ItemStatus()
        {
            quantity = 0;
            subStatuses = new List<ItemStatus>();
        }

        public ItemStatus(ItemStatus fromStatus)
        {
            quantity = 0;
            subStatuses = new List<ItemStatus>();
            if (fromStatus != null)
            {
                lastUpdated = DateTime.Now;
                this.lifetime = fromStatus.lifetime;
                this.timeAvailable = fromStatus.timeAvailable;
                this.mostRecentChange = fromStatus.mostRecentChange;
                this.birthDate = fromStatus.birthDate;
                if (fromStatus.lastUpdated != new DateTime(1, 1, 1, 0, 0, 0))
                    tempTime = lastUpdated - fromStatus.lastUpdated;
            }
        }

        public ItemStatus(int quantity, string location)
        {
            this.quantity = quantity;
            this.location = location;
        }

        public ItemStatus(SerializationInfo info, StreamingContext ctxt)
        {
            quantity = (int)info.GetValue("quantity", typeof(int));
            location = (string)info.GetValue("location", typeof(string));
            mostRecentChange = (DateTime)info.GetValue("MostRecentChange",typeof(DateTime));
            lastUpdated = (DateTime)info.GetValue("LastUpdated", typeof(DateTime));
            subStatuses = (List<ItemStatus>)info.GetValue("subStatuses", typeof(List<ItemStatus>));
            birthDate = (DateTime)info.GetValue("birthDate", typeof(DateTime));
            timeAvailable = (TimeSpan)info.GetValue("timeAvailable", typeof(TimeSpan));
            lifetime = (TimeSpan)info.GetValue("lifetime", typeof(TimeSpan));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            //You can use any custom name for your name-value pair. But make sure you
            // read the values with the same name. For ex:- If you write EmpId as "EmployeeId"
            // then you should read the same with "EmployeeId"
            info.AddValue("quantity", quantity);
            info.AddValue("location", location);
            info.AddValue("MostRecentChange", mostRecentChange);
            info.AddValue("LastUpdated", lastUpdated);
            info.AddValue("subStatuses", subStatuses);
            info.AddValue("birthDate", birthDate);
            info.AddValue("timeAvailable", timeAvailable);
            info.AddValue("lifetime", lifetime);
        }

        public ItemStatus Finalize()
        {
            if (tempTime > TimeSpan.FromMinutes(10))
                return this;
            if (IsAvaliable)
            {
                timeAvailable += tempTime;
            }
            lifetime += tempTime;
            return this;
        }

        public void Add(ItemStatus newStatus)
        {
            if (subStatuses == null)
                return;
            subStatuses.Add(newStatus);
            this.quantity += newStatus.quantity;
            if (subStatuses.Count == 1)
                this.location = newStatus.location;
            else
                this.location = null;
        }

        public void Add(int quantity, string location)
        {
            Add(new ItemStatus(quantity, location));
        }

        public override bool Equals(object obj)
        {
            if (obj == null)
                return false;
            if (obj.GetType() != this.GetType())
                return base.Equals(obj);
            else
            {
                ItemStatus that = (ItemStatus)obj;
                if (this.quantity != that.quantity)
                    return false;
                if(this.location!=null)
                    return this.location==that.location && this.quantity == that.quantity;
                foreach(ItemStatus s in this.subStatuses)
                {
                    if (!that.subStatuses.Contains(s))
                        return false;
                }
                return true;
            }
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return ToString(false);
        }

        public string ToString(bool onlyShowAvailable)
        {
            string value = "";
            if (location != null && subStatuses==null)
                return quantity + " " + location;
            if (subStatuses == null)
                return "error: subStatuses is null";
            foreach (ItemStatus s in subStatuses)
            {
                if (onlyShowAvailable && !s.IsAvaliable)
                    continue;
                value += s + "\r\n";
            }
            if (mostRecentChange == DateTime.MinValue)
                value = value.Trim();
            else value += "Most Recent Change: " + ((mostRecentChange.Date == DateTime.Today) ? mostRecentChange.ToLongTimeString() : mostRecentChange.ToShortDateString());
            double percent;
            if (lifetime.TotalSeconds > 0)
                percent = (double)timeAvailable.TotalSeconds / (double)lifetime.TotalSeconds;
            else percent = IsAvaliable ? 1 : 0;
            return value + (lastUpdated == DateTime.MinValue ? "" : "\r\nLast updated: " + lastUpdated.ToLongTimeString()) + "\r\nAvailable " + percent.ToString("0.##%") + " of the time since " + birthDate.ToShortDateString() + "\r\nLifetime: " + lifetime;
        }

        public double PercentAvailable
        {
            get
            {
                double percent;
                if (lifetime.TotalSeconds > 0)
                    percent = (double)timeAvailable.TotalSeconds / (double)lifetime.TotalSeconds;
                else percent = IsAvaliable ? 1 : 0;
                return percent;
            }
        }

        public bool IsAvaliable
        {
            get
            {
                if (location != null)
                {
                    string temp = location.ToLower();
                    return !(temp.Contains("checked out") || temp.Contains("being transferred between libraries") || temp.Contains("on order") || temp.Contains("being repaired") || temp.Contains("held for pickup"));
                }
                if (subStatuses == null)
                    return false;
                return subStatuses.Exists(s => s.IsAvaliable);
            }
        }

        public DateTime MostRecentChange
        {
            get
            {
                return mostRecentChange;
            }
            set
            {
                mostRecentChange = value;
            }
        }

        public int Quantity
        {
            get
            {
                return quantity;
            }
        }

        public string Location
        {
            get
            {
                return location;
            }
        }

        public ItemStatus[] Statuses
        {
            get
            {
                return subStatuses.ToArray();
            }
        }

        public DateTime BirthDate
        {
            get
            {
                return birthDate;
            }
        }

        public DateTime LastUpdated
        {
            get
            {
                return lastUpdated;
            }
        }

        internal void Correct()
        {
            if (birthDate == new DateTime(1, 1, 1, 0, 0, 0))
                birthDate = DateTime.Today;
            if (timeAvailable > lifetime)
            {
                timeAvailable = lifetime = new TimeSpan(0);
                birthDate = DateTime.Today;
            }
        }

        public void ResetStatistics()
        {
            tempTime = new TimeSpan(0);
            birthDate = DateTime.Now;
            lifetime = new TimeSpan(0);
            timeAvailable = new TimeSpan(0);
            lastUpdated = DateTime.Now;
        }
    }
}
